<template>
  <div class="deploy">
    <el-row>
      <!-- 头部1 -->
      <el-col :span="24">
        <div>
          <el-card class="deploy-head-card" shadow="never" :body-style="{padding:  '10px'}">
            <el-row>
              <!-- 命名空间 -->
              <el-col :span="6">
                <div style="display: table-row">
                  <span>命名空间 : </span>
                  <el-select v-model="namespaceValue" filterable placeholder="请选择">
                    <el-option
                    v-for="(item,index) in namespaceList"
                    :key="index"
                    :label="item.metadata.name"
                    :value="item.metadata.name">
                    </el-option>
                  </el-select>
                </div>
              </el-col>
              <!-- 刷新按钮 -->
              <el-col :span="2" :offset="16">
                <div>
                  <el-button style="botder-radius:2px" icon="Refresh" plain @click="getDeployments()">刷新</el-button>
                </div>
              </el-col>
            </el-row>
          </el-card>
        </div>
      </el-col>
      <!-- 头部2 -->
      <el-col :span="24">
        <div>
          <el-card class="deploy-head-card" shadow="never" :body-style="{padding:'10px'}">
            <el-row>
              <!-- 创建按钮 -->
              <el-col :span="2" >
                <div>
                  <el-button style="border-radius: 2px;"
                  icon="Edit"
                  type="primary"
                  v-loading.fullscreen.lock="fullscreenLoading"
                  @click="createDeploymentDrawer = true"> 创建
                  </el-button>
                </div>
              </el-col>
              <!-- 搜索框和搜索按钮 -->
              <el-col :span="6">
                <div>
                  <el-input class="deploy-head-search"
                  clearable placeholder="请输入"
                  v-model="searchInput"></el-input>
                  <el-button style="border-radius:2px;"
                  icon="Search" type="primary" plain @click="getDeployments()"></el-button>
                </div>

              </el-col>
            </el-row>
          </el-card>
        </div>
      </el-col>
      <!-- 数据表格 -->
      <el-col :span="24">
      <div>
        <el-card class="deploy-body-card" shadow="never" :body-style="{padding: '5px'}">
          <el-table style="width: 100%;font-size:12px;margin-bottom:10px" :data="deploymentList">
              <el-table-column width="20"></el-table-column>
              <el-table-column align="left" label="Deployment名" v-model="namespaceAppValue">
                  <template v-slot="scope" >
                    <a class="deploy-body-deployname" >
                        <div @click="getDeploymentpods(scope.row.metadata.name)">{{ scope.row.metadata.name }}</div>
                    </a>
                  </template>
              </el-table-column>
              <el-table-column align="left" label="标签">
                <template v-slot="scope">
                  <div v-for="(val,key) in scope.row.metadata.labels" :key="key">
                    <el-popover
                    placement="right"
                    :width="200"
                    trigger="hover"
                    :content="key + ':' + val">
                      <template #reference>
                        <!-- 标签与标签之间间隔设置为5px -->
                        <el-tag style="margin-bottom:5px" type="warning"
                        >{{ ellipsis(key + ':' + val) }}</el-tag>
                      </template>
                    </el-popover>
                  </div>
                </template>
              </el-table-column>
               <!-- 容器组 -->
              <el-table-column align=center label="容器组">
                  <!-- 可用数量/总数量,三元运算，若值大于0则显示值，否则显示0 -->
                  <template v-slot="scope">
                      <span>{{ scope.row.status.availableReplicas>0?scope.row.status.availableReplicas:0  }} / {{ scope.row.spec.replicas>0?scope.row.spec.replicas:0 }} </span>
                  </template>
              </el-table-column>
              <!-- 创建时间 -->
              <el-table-column align=center min-width="100" label="创建时间">
                  <!-- timeTrans函数用于将格林威治时间转成北京时间 -->
                  <template v-slot="scope">
                      <el-tag type="info">{{ timeTrans(scope.row.metadata.creationTimestamp) }} </el-tag>
                  </template>
              </el-table-column>
              <!-- 容器镜像 -->
              <el-table-column align=center label="镜像">
                  <!-- 与label的显示逻辑一致 -->
                  <template v-slot="scope">
                      <div v-for="(val, key) in scope.row.spec.template.spec.containers" :key="key">
                          <el-popover
                              placement="right"
                              :width="200"
                              trigger="hover"
                              :content="val.image">
                              <template #reference>
                                  <el-tag style="margin-bottom: 5px">{{ ellipsis(val.image.split('/')[2]==undefined?val.image:val.image.split('/')[2]) }}</el-tag>
                              </template>
                          </el-popover>
                      </div>
                  </template>
              </el-table-column>
              <!-- 操作列，放按钮 -->
              <el-table-column align=center label="操作" width="400">
                  <template v-slot="scope">
                      <el-button size="small" style="border-radius:2px;" icon="Edit" type="primary" plain @click="getDeploymentDetail(scope)">YAML</el-button>
                      <el-button size="small" style="border-radius:2px;" icon="Plus" type="primary" @click="handleScale(scope)">扩缩</el-button>
                      <!-- <el-button size="small" style="border-radius:2px;" icon="RefreshLeft" type="primary" @click="handleConfirm(scope,'重启')">重启</el-button> -->
                      <el-button size="small" style="border-radius:2px;" icon="RefreshLeft" type="primary" @click="handleConfirm(scope, '重启', restartDeployment)">重启</el-button>
                      <el-button size="small" style="border-radius:2px;" icon="Delete" type="danger" @click="handleConfirm(scope, '删除', delDeployment)">删除</el-button>
                  </template>
              </el-table-column>
          </el-table>
          <!-- size-change 单页大小改变后触发
          current-change 页数改变后触发
          current-page 当前页，跟后端page一致
          page-size 单页大小，跟后端limit一致 -->
          <el-pagination
            class="deploy-body-pagination"
            background
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="currentPage"
            :page-sizes="pagesizeList"
            :page-size="pagesize"
            layout="total,sizes,prev,pager,next,jumper"
            :total="deploymentTotal"></el-pagination>
        </el-card>
      </div>

      </el-col>
    </el-row>
    <!-- 新建 -->
    <el-drawer
    v-model="createDeploymentDrawer"
    :direction="direction"
    :before-close="handleClose">
      <!-- 标题 -->
      <template #title>
        <h4 align="center">创建Deployment</h4>
      </template>
      <!-- body，填写表单属性 -->
      <template #default>
        <!-- flex布局，居中 -->
        <el-row type="flex" justify="center">
          <el-col :span="20">
            <el-form ref="createDeployment"
            :rules="createDeploymentRules"
            :model="createDeployment"
            label-width="80px">
              <el-form-item class="deploy-create-form" label="名称" prop="name">
                <el-input v-model="createDeployment.name"></el-input>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="命名空间" prop="namespace">
                <el-select v-model="createDeployment.namespace" filterable placeholder="请选择">
                  <el-option
                  v-for="(item,index) in namespaceList"
                  :key="index"
                  :label="item.metadata.name"
                  :value="item.metadata.name">
                  </el-option>
                </el-select>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="副本数" prop="replicas">
                <el-input-number v-model="createDeployment.replicas" :min="1" :max="10"></el-input-number>
                <el-popover
                placement="top"
                :width="100"
                trigger="hover"
                content="申请副本数上限为10个">
                  <template #reference>
                    <el-icon style="width:2em;font-size:18px;color:#4795EE"><WarningFilled/></el-icon>
                  </template>
                </el-popover>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="镜像" prop="image">
                <el-input v-model="createDeployment.image"></el-input>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="启动命令" prop="command">
                <el-input v-model="createDeployment.command"></el-input>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="标签" prop="label_str">
                <el-input v-model="createDeployment.label_str" placeholder="示例 : project=ms,app=nginx"></el-input>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="资源配额" prop="resource">
                <el-select v-model="createDeployment.resource" placeholder="请选择">
                  <el-option value="0.5/1" label="0.5C1G"></el-option>
                  <el-option value="1/2" label="1C2G"></el-option>
                  <el-option value="2/4" label="2C4G"></el-option>
                  <el-option value="4/8" label="4C8G"></el-option>
                </el-select>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="容器端口" prop="container_port">
                <el-input v-model="createDeployment.container_port" placeholder="示例 : 80"></el-input>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="健康检查">
                <el-switch v-model="createDeployment.health_check"></el-switch>
              </el-form-item>
              <el-form-item class="deploy-create-form" label="检查路径">
                <el-input v-model="createDeployment.health_path" placeholder="示例 : /healthcheck"></el-input>
              </el-form-item>
            </el-form>
          </el-col>
        </el-row>
      </template>
      <!-- footer，处理提交和取消 -->
      <template #footer>
        <div style="height: 50px">
        <el-button @click="createDeploymentDrawer = false">取消</el-button>
        <el-button type="primary" @click="submitForm('createDeployment')">立即创建</el-button>
        </div>
      </template>
    </el-drawer>
   
    <!-- YAML -->
    <el-dialog title="YAML信息" v-model="yamlDialog" width="60%" top="2%">
      <codemirror
          :value="contentYaml"
          border
          :options="cmOptions"
          height="500"
          style="font-size: 14px;"
          @change="onChange">
      </codemirror>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="yamlDialog = false">取消</el-button>
          <el-button type="primary" @click="updateDeployment()">更新</el-button>
        </span>
      </template>
    </el-dialog>

    
    
    <!-- 调整副本数的弹框 -->
    <el-dialog title="副本数调整" v-model="scaleDialog" width="25%">
      <div style="text-align:center">
          <span>实例数: </span>
          <el-input-number :step="1" v-model="scaleNum" :min="0" :max="30" label="描述文字"></el-input-number>
      </div>
      <template #footer>
          <span class="dialog-footer">
              <el-button @click="scaleDialog = false">取 消</el-button>
              <el-button type="primary" @click="scaleDeployment()">更 新</el-button>
          </span>
      </template>
    
    </el-dialog>
    <!-- 重启的弹框 -->
    <!-- 不需要写重启的弹框，因为使用了handleConfirm的弹框 -->
  </div>
  
</template>

<script>
import common from "../common/Config";
import httpClient from "../../utils/request";
import yaml2obj from 'js-yaml';
import json2yaml from "json2yaml";

export default {
  data() {
    return {
      // 主要配置
      defaultOpts: {
       // 编辑器的值
        value: '',
         // 编辑器主题：vs, hc-black, or vs-dark，更多选择详见官网
        theme: 'vs-dark',
        // 右侧不显示编辑器预览框
        roundedSelection: true, 
        // 自动缩进
        autoIndent: true ,
        // 是否只读
        readOnly: false, 
        // 语言类型java,c,php更多选择详见官网
        language: 'yaml', 
      },
        // 编辑器对象
      monacoEditor: {},

      //搜索
      searchInput: '',
      // 命名空间属性
      namespaceValue: 'default',
      namespaceList:  [],
      namespaceListUrl: common.k8sNamespaceList,

      // 选择ns对应的app
      namespaceAppValue: '',
      // 创建
      createDeploymentDrawer: false,
      fullscreenLoading: false,
      direction: 'rtl',
      //创建的deployment属性
      createDeployment: {
        name: '',
        namespace: '',
        replicas: 1,
        image: '',
        command: '',
        resource: '',
        health_check: false,
        health_path: '',
        label_str: '',
        label: {},
        container_port: '',
      },
      //发送请求时的参数
      createDeploymentData: {
        url: common.k8sDeploymentCreate,
        params: {}
      },
      // 创建deployment的表单校验规则
      createDeploymentRules: {
        name: [{
          required: true,
          message: '请填写名称',
          trigger: 'change',
        }],
        image: [{
          required: true,
          message: '请填写镜像',
          trigger: 'change',
        }],
        command: [{
          required: true,
          message: '请填写容器启动命令',
          trigger: 'change',
        }],
        namespace: [{
          required: true,
          message: '请选择命名空间',
          trigger: 'change',
        }],
        resource: [{
          required: true,
          message: '请选择配额',
          trigger: 'change',
        }],
        label_str: [{
          required: true,
          message: '请填写标签',
          trigger: 'change',
        }],
        container_port: [{
          required: true,
          message: '请填写容器端口',
          trigger: 'change',
        }],
      },
      // 列表
      apploading: false,
      deploymentList: [],
      deploymentTotal: 0,
      getDeploymentData: {
          url: common.k8sDeploymentList,
          params: {
            filter_name: '',
            namespace: '',
            page: 1,
            limit: 10
          }
      },
      //分页
      currentPage: 1,
      pagesize: 10,
      pagesizeList: [10,20,30],
      // 编辑器配置
      cmOptions: common.cmOptions,
      contentYaml: '',

      // 详 情
      yamlDialog: false,
      deploymentDetail: {},
      getdeploymentDetailData: {
        url: common.k8sDeploymentDetail,
        params: {
          // deployment_name: '',
          deployment: '',
          namespace: ''
        }
      },
      
      // 更新
      updateDeploymentData: {
        url: common.k8sDeploymentUpdate,
        params: {
          content: '',
          namespace: ''
        }
      },
       //扩缩容
      scaleNum: 0,
      scaleDialog: false,
      scaleDeploymentData: {
          url: common.k8sDeploymentScale,
          params: {
              deployment: '',
              namespace: '',
              // 跟后端参数名称保持了一致
              scalenumber: ''
          }
      },
      //重启
      restartDialog: false,
      restartDeploymentData: {
          url: common.k8sDeploymentRestart,
          params: {
              deployment: '',
              namespace: '',
          }
      },
      //删除
      delDialog: false,
      delDeploymentData: {
          url: common.k8sDeploymentDel,
          params: {
              deployment: '',
              namespace: '',
          }
      },
    }
  },
  methods: {
    getDeploymentpods(e){
      //跳转至Pod页面
      localStorage.setItem('namespaceApp',e);
      this.$router.push('/workload/pod');
    },
    //json to yaml 
    transYaml(content) {
      return json2yaml.stringify(content)
    },
    transObj(content) {
      return yaml2obj.load(content)
    },
    // 编辑器内容发生变化时触发的方法
    onChange(val) {
      this.contentYaml = val
    },
    //分页
    handleSizeChange(size) {
      this.pagesize = size
      this.getDeployments()
    },
    handleCurrentChange(currentPage) {
      this.currentPage = currentPage
      this.getDeployments()
    },
    //格林威治时间转为北京时间
    timeTrans(timestamp) {
        let date = new Date(new Date(timestamp).getTime() + 8 * 3600 * 1000)
        date = date.toJSON();
        date = date.substring(0, 19).replace('T', ' ')
        return date 
    },
    //字符串的截取
    ellipsis(value) {
      return value.length>15?value.substring(0,15)+'...':value
    },
    // 处理抽屉的关闭，double check，增加体验
    handleClose(done){
      this.$confirm('确认关闭')
      .then(()=> {
        done();
      })
      .catch(()=>{})
    },
    getNamespacesList() {
      httpClient.get(this.namespaceListUrl)
      .then(res => {
        this.namespaceList = res.data.items
      })
      .catch(res => {
        this.$message.error({
          message: res.msg
        })
      })
    },
    // 创建deployment
    resetForm(formName) {
      this.$refs[formName].resetFields()
    },
    submitForm(formName){
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.createDeployFunc()
        } else {
          return false
        }
      })
    },
    createDeployFunc() {
      // 正则匹配，验证label
      let reg = new RegExp("(^[A-Za-z]+=[A-Za-z0-9]+).*")
      if (!reg.test(this.createDeployment.label_str)) {
        this.$message.warning({
          message: "标签填写异常，请确认后重新填写"
        })
      }
      this.fullscreenLoading = true
      // 处理label，cpu和mem
      let label = new Map()
      let cpu,memory
      let a = (this.createDeployment.label_str).split(',')
      a.forEach(item => {
        let b= item.split('=')
        label[b[0]] = b[1]
      })
      let resourceList = this.createDeployment.resource.split("/")
      cpu = resourceList[0]
      memory = resourceList[1] + "Gi"

      // 前端给后端传字符串数组
      var command2 = ["/bin/sh","-c",this.createDeployment.command]

      // 赋值
      this.createDeploymentData.params = this.createDeployment
      this.createDeploymentData.params.container_port = parseInt(this.createDeployment.container_port)
      this.createDeploymentData.params.label = label
      this.createDeploymentData.params.cpu = cpu
      this.createDeploymentData.params.memory = memory
      this.createDeploymentData.params.command = command2

      // 发起请求
      httpClient.post(this.createDeploymentData.url, this.createDeploymentData.params)
      .then(res => {
        this.$message.success({
          message: res.msg
        })
        // 重新获取列表
        this.getDeployments()
      })
      .catch(res => {
        this.$message.error({
          message: res.msg
        })
      })
      //重置表单
      this.resetForm('createDeployment')
      this.fullscreenLoading = false
      this.createDeploymentData = false
    },
    // 获取deployment 列表
    getDeployments(){
      this.apploading = true
      this.getDeploymentData.params.filter_name = this.searchInput
      this.getDeploymentData.params.namespace = this.namespaceValue
      this.getDeploymentData.params.page = this.currentPage
      this.getDeploymentData.params.limit = this.pagesize
      httpClient.get(this.getDeploymentData.url,{params: this.getDeploymentData.params})
      .then(res => {
        this.deploymentList = res.data.items
        this.deploymentTotal = res.data.total

      })
      .catch(res => {
        this.$message.error({
          message: res.msg
        })
      })
      this.apploading = false
    },
    // 获取详情, e参数标识传入的scope插槽，.row是该行的数据
    getDeploymentDetail(e) {
      this.getdeploymentDetailData.params.deployment = e.row.metadata.name
      // 由于我写的后端传参名字是deployment
      // this.DeploymentDetailData.params.deployment_name = e.row.metadata.name
      this.getdeploymentDetailData.params.namespace = this.namespaceValue
      httpClient.get(this.getdeploymentDetailData.url,{params: this.getdeploymentDetailData.params})
      .then(res => {
        this.deploymentDetail = res.data
        this.contentYaml = this.transYaml(this.deploymentDetail)
        this.yamlDialog = true
      })
      .catch(res => {
        this.$message.error({
          message: res.msg
        })
      })
    },
    //更新
    updateDeployment(){
      let content = JSON.stringify(this.transObj(this.contentYaml))
      this.updateDeploymentData.params.content = content
      this.updateDeploymentData.params.namespace = this.namespaceValue
      // post请求不能使用{params: this.updateDeploymentData.params} 这种形式
      httpClient.put(this.updateDeploymentData.url,this.updateDeploymentData.params)
      .then(res => {
        this.$message.success({
          message: res.msg
        })
        // 刷新列表
        this.getDeployments()
      })
      .catch(res => {
        this.$message.error({
          message: res.msg
        })
      })
      this.yamlDialog =  false
    },
    //扩缩容的中间方法，用于赋值及打开弹出框
    handleScale(f) {
        this.scaleDialog = true
        this.deploymentDetail = f.row
        this.scaleNum = f.row.spec.replicas
    },
    //扩缩容deployment
    scaleDeployment() {
        this.scaleDeploymentData.params.deployment = this.deploymentDetail.metadata.name
        this.scaleDeploymentData.params.namespace = this.namespaceValue
        this.scaleDeploymentData.params.scalenumber = this.scaleNum
        httpClient.post(this.scaleDeploymentData.url, this.scaleDeploymentData.params)
        .then(res => {
            this.$message.success({
            message: res.msg
            })
        })
        .catch(res => {
            this.$message.error({
            message: res.msg
            })
        })
        //关闭弹出框
        this.scaleDialog = false
        //更新后重新获取列表
        setTimeout(()=>{
          this.getDeployments()
        },1000);
    },

    // 更新，删除
    //弹出确认框，用于危险操作的double check
    //obj是行数据（scope相当于一行的数据， scope.row相当于当前行的数据对象），opeateName是操作名，fn是操作的方法
    handleConfirm(obj,operateName,fn) {
    // handleConfirm( operateName) {
        this.confirmContent = '确认继续 ' + operateName + ' 操作吗？'
        //$confirm用于弹出确认框
        this.$confirm(this.confirmContent,'提示',{
            confirmButtonText: '确定',
            cancelButtonText: '取消',
        })
        .then(() => {
          // 需要传入obj，否则下面无法拿到数据
          fn(obj)
          // 如果没有传入fn，可以使用下面这种方法
          // this.restartDeployment(obj)
        })
        .catch(() => {
            this.$message.info({
                message: '已取消操作'
            })          
        })
    },
    // 重启方法
    restartDeployment(obj){
      this.restartDeploymentData.params.deployment = obj.row.metadata.name
      this.restartDeploymentData.params.namespace = this.namespaceValue
      httpClient.post(this.restartDeploymentData.url, this.restartDeploymentData.params)
      .then(res => {
          this.$message.success({
          message: res.msg
          })
      })
      .catch(res => {
          this.$message.error({
          message: res.msg
          })
      })
      // //关闭弹出框
      // this.scaleDialog = false
    },
    //删除方法
    delDeployment(obj){
      this.delDeploymentData.params.deployment = obj.row.metadata.name
      this.delDeploymentData.params.namespace = this.namespaceValue
      httpClient.delete(this.delDeploymentData.url, {params: this.delDeploymentData.params})
      .then(res => {
          this.$message.success({
          message: res.msg
          })
      })
      .catch(res => {
          this.$message.error({
          message: res.msg
          })
      })
      // //关闭弹出框
      // this.delDialog = false
      //更新后重新获取列表
  
      this.getDeployments()
    }
    

  },
  watch: {
    namespaceValue: {
      handler(){
        localStorage.setItem('namespace',this.namespaceValue)
        this.currentPage = 1
        this.getDeployments()
      }
    },
    namespaceAppValue: {
      handler(){
        localStorage.setItem('namespaceApp','111')
        this.currentPage = 1
      }
    }
  },
  beforeMount(){
    if(localStorage.getItem('namespace') !== undefined && localStorage.getItem('namespace') !== null){
      this.namespaceValue = localStorage.getItem('namespace')
    }
    this.getNamespacesList()
    this.getDeployments()
  },
}

</script>

<style scoped>
  .deploy-head-card, .deploy-body-card {
    border-radius: 1px;
    margin-bottom: 5px;
  }
  .deploy-head-search {
    width: 160px;
    margin-right: 10px;
  }
  /* 数据表格deployment名颜色 */
  .deploy-body-deployname {
    color: #4795EE;
  }
  /* deploy名鼠标悬停 */
  .deploy-body-deployname:hover {
    color: rgb(84, 138,238);
    cursor: pointer;
    font-weight: bold;
  }

</style>